/*********************************************************************************
 *      Copyright:  (C) 2025 LingYun IoT System Studio
 *                  All rights reserved.
 *
 *       Filename:  client.c
 *    Description:  This file 
 *                 
 *        Version:  1.0.0(30/04/25)
 *         Author:  chenhongyu0822@163.com
 *      ChangeLog:  1, Release initial version on "30/04/25 22:10:23"
 *                 
 ********************************************************************************/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <unistd.h>
#include <netdb.h> 
#include <netinet/tcp.h> 


#include "logger.h"
#include "client.h"


void print_usage(char *program)
{
    printf("Usage:%s [OPTION]...\n", program);
    printf("%s is a socket client program\n", program);
    printf("-p[port] socket server port address\n");
    printf("-i[addr] socket server IP address\n");
    printf("-t[time] data upload time\n");
    printf("-h[help] display this help information\n");
    printf("\nExample:%s -p 9000 -i 192.168.0.2 -t 30\n", program);
    return;
}

int client_init(socket_ctx_t *sock, char * ip, int  port)
{
	if( !sock || port<=0 )
	{
		return -1;
	}
	memset( sock, 0, sizeof(*sock) );
	sock->fd = -1;
	sock->port = port;
	if( ip )
	{
		strncpy(sock->ip, ip, HOSTNAME_LEN);
	}
	return 0;

}

int client_connect(socket_ctx_t *sock)
{
    int                           connect_fd;
    struct sockaddr_in            serv_addr;
    struct addrinfo               hints;
	struct addrinfo               *res;
	struct addrinfo               *p;
	struct in_addr                ip_addr;
	char                          port_str[6];
	int                           success=0;

	if(! sock)
	{
		return -1;
	}
	//尝试直接解析IP
	if(inet_aton(sock->ip, &ip_addr))
	{
		connect_fd=socket(AF_INET, SOCK_STREAM, 0);
     	if(connect_fd<0)
     	{
			log_error("create socket failure:%s\n",   strerror(errno));
         	return -2;
     	}
        //如果是IP地址，直接连接
    	memset(&serv_addr, 0, sizeof(serv_addr));
    	serv_addr.sin_family=AF_INET;
    	serv_addr.sin_port=htons(sock->port);
		serv_addr.sin_addr=ip_addr;

    	if(connect(connect_fd, (struct sockaddr *)&serv_addr, sizeof(serv_addr))<0)
    	{
			log_error("connect to server[%s:%d] failure:%s\n", sock->ip, sock-> port, strerror(errno));
			close(connect_fd);
			return -2;
    	}
	}
	else
	{
		//如果是域名，则进行域名解析
		memset(&hints, 0, sizeof(hints));
		hints.ai_family=AF_INET;
		hints.ai_socktype=SOCK_STREAM;
		snprintf(port_str, sizeof(port_str), "%d", sock->port);

		if(getaddrinfo(sock->ip, port_str, &hints, &res)!=0)
		{
			log_error("DNS resolution failed for:%s\n", sock->ip);
			return -3;
		}

		for(p=res; p!=NULL; p=p->ai_next)
		{
			connect_fd=socket(p->ai_family, p->ai_socktype, p->ai_protocol);
			if(connect_fd<0)
				continue;

			if((connect(connect_fd, p->ai_addr, p->ai_addrlen))==0)
			{
				success=1;
				break;//连接成功
			}

			close(connect_fd);//连接失败 关闭并进行下一个
		}

		freeaddrinfo(res);

		if(!success)
		{
			log_error("connect to server[%s:%d] failure:%s\n", 
					sock->ip, sock->port, strerror(errno));
			return -4;
		}
	} 

	printf("connect to server[%s:%d] successfully!\n", sock->ip, sock->port);
	log_info("connect to server[%s:%d] successfully!\n", sock->ip, sock->port);
	sock->fd=connect_fd;
	sock->connectd=1;

	return connect_fd;
}

int socket_check(socket_ctx_t *sock)
{
	struct tcp_info       info;
	socklen_t             len=sizeof(info);

	if(! sock)
	{
		return -1;
	}

	if (sock->fd < 0) 
	{
		log_error("Invalid socket descriptor.\n");
        return -1;
    }

	if(getsockopt(sock->fd, IPPROTO_TCP, TCP_INFO, &info, &len)<0)
	{
		log_error("getsockopt error:%s\n", strerror(errno));
		printf("getsockopt error:%s\n", strerror(errno));
		return -2;
	}

	if(info.tcpi_state == TCP_ESTABLISHED)
		sock->connectd=1;
	else
		sock->connectd=-1;
	return sock->connectd;
}

int client_send(socket_ctx_t *sock, char *data, int size)
{
	int          rv;
	char         buf[1024];

	if((rv=write(sock->fd, data, size)) < 0)
	{
		log_error("Write to server failure:%s\n", strerror(errno));
		return -1;
	}

	memset(buf, 0, sizeof(buf));
	rv=read(sock->fd, buf, sizeof(buf));
	if(rv<=0)
    {
		log_error("Read data from server failure:%s\n", strerror(errno));
		return -2;
    }
	else
    {
		log_info("Read %d bytes data from server: %s\n", rv, buf);
	}

	return 0;
}

